package core

import (
	"encoding/hex"
	"errors"
	"net/mail"
	"strconv"
	"strings"

	"xdes"

	"warnmonitor/common"
	"warnmonitor/db"
	"warnmonitor/email"

	"github.com/astaxie/beego"
)

var (
	Des                 = "des"
	TripleDes           = "3des"
	Aligorthms          = []string{Des, TripleDes}
	CipherTextSeparator = "|"
)

var (
	sysKey string //密钥
)

func IsSupportAligorthm(name string) bool {
	for _, v := range Aligorthms {
		if strings.ToLower(name) == v {
			return true
		}
	}
	return false
}

//加密数据
//data, key, aligorthm
func Encrypt(data, key, aligorthm string) (string, error) {
	if !IsSupportAligorthm(aligorthm) {
		return "", errors.New(aligorthm + " encrypt/decrypt aligorthm not support .")
	}
	var realkey []byte
	var realData []byte = []byte(data)
	var security []byte
	var err error = errors.New("Gen CipherText failed.")

	if strings.ToLower(aligorthm) == Des {
		realkey = xdes.FixedKeySize([]byte(key), 8, 0)
		security, err = xdes.DesEncrypt(realData, realkey)
	}
	if strings.ToLower(aligorthm) == TripleDes {
		realkey = xdes.FixedKeySize([]byte(key), 24, 0)
		security, err = xdes.TripleDesEncrypt(realData, realkey)
	}

	if err != nil {
		return "", err
	} else {
		return hex.EncodeToString(security), nil
	}
}

//解密数据
//data, key, aligorthm
func Decrypt(data, key, aligorthm string) (string, error) {
	if !IsSupportAligorthm(aligorthm) {
		return "", errors.New(aligorthm + " encrypt/decrypt aligorthm not support .")
	}
	realData, err := hex.DecodeString(data)
	if err != nil {
		return "", errors.New("config " + data + " decode failed.")
	}
	var realkey []byte
	var plain []byte
	if strings.ToLower(aligorthm) == Des {
		realkey = xdes.FixedKeySize([]byte(key), 8, 0)
		plain, err = xdes.DesDecrypt(realData, realkey)
	}
	if strings.ToLower(aligorthm) == TripleDes {
		realkey = xdes.FixedKeySize([]byte(key), 24, 0)
		plain, err = xdes.TripleDesDecrypt(realData, realkey)
	}
	if err != nil {
		return "", err
	}
	return string(plain), nil
}

func readConfigItemValue(itemKey string) string {
	itemValue := beego.AppConfig.String(itemKey)
	if itemValue != "" {
		if IsCipherTextConfigItem(itemValue) {
			if sysKey == "" {
				panic("Please start server setting value of optional --key, -k .")
			} else {
				value, err := decryptConfigItem(itemValue)
				if err == nil {
					return value
				} else {
					panic(err.Error())
				}
			}
		} else {
			return itemValue
		}
	} else {
		panic("Please setting " + itemKey + " failed's value in your " + beego.AppConfigPath + " .")
	}
}

func IsCipherTextConfigItem(itemValue string) bool {
	for _, aligorthm := range Aligorthms {
		if strings.HasPrefix(itemValue, aligorthm+CipherTextSeparator) {
			return true
		}
	}
	return false
}

func decryptConfigItem(itemValue string) (plainText string, err error) {
	v := []rune(itemValue)
	if strings.HasPrefix(itemValue, Des+CipherTextSeparator) {
		return Decrypt(string(v[len(Des+CipherTextSeparator):]), sysKey, Des)
	} else if strings.HasPrefix(itemValue, TripleDes+CipherTextSeparator) {
		return Decrypt(string(v[len(TripleDes+CipherTextSeparator):]), sysKey, TripleDes)
	} else {
		return "", errors.New(itemValue + " format is invalid . ")
	}
}

func InitEmailConfigInfo(key string) {

	sysKey = key

	email.Config.Username = readConfigItemValue(email.EMAIL_USERNAME)
	email.Config.Password = readConfigItemValue(email.EMAIL_PASSWORD)
	email.Config.Host = readConfigItemValue(email.EMAIL_HOST)

	port := readConfigItemValue(email.EMAIL_PORT)
	if port, err := strconv.Atoi(port); err == nil {
		email.Config.Port = uint16(port)
	} else {
		panic("Please setting port use a number and not using.")
	}

	from := readConfigItemValue(email.EMAIL_FROM)

	if address, err := mail.ParseAddress(from); err == nil {
		email.Config.From = *address
		if err := email.Config.Init(); err == nil {
			beego.Info("Email Config init ok.")
			beego.Info("Warnmonitor use Admin email is :" + address.Name + " <" + address.Address + ">")
		} else {
			panic("Email Config init failed.")
		}
	} else {
		panic("AppConfig " + email.EMAIL_FROM + " setting use format 'jack <jack@gmail.com>'.")
	}

}

func Notify(wd *common.WarnData) *common.RetVal {
	rd := wd.CheckWarnData()
	if rd.HasError() {
		return rd
	} else {
		var emailNotify = new(email.EmailNotify)
		emailNotify.Appname = wd.Appname
		emailNotify.Subject = wd.Subject
		emailNotify.Content = wd.Content
		emailNotify.IsHtmlContent = wd.Html
		var to = make([]string, len(wd.To))
		for _, dest := range wd.To {
			to = append(to, dest.Email)
		}
		emailNotify.To = to
		emailNotify.SynSend()

		var notifyInfo = &db.NotifyInfo{
			Appname: wd.Appname,
			Subject: wd.Subject,
			Content: wd.Content,
		}
		_, err := notifyInfo.Save()
		if err == nil {
			return common.NotifySuccessful
		} else {
			return common.NotifyFailed
		}
	}
}
